package com.zf.easyexcel.utils.handler;


import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.springframework.util.CollectionUtils;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;

/**
 * 自定义自动列宽写入样式策略。
 * easyexcel内部定义的 {@link com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy } 也是类似的实现
 */
public class CustomAutoColumnWidthWriteHandler extends AbstractColumnWidthStyleStrategy {

    private final HashMap<Integer, HashMap<Integer, Double>> CACHE_COLUMN_WIDTH = new HashMap<>();

    /**
     * 指定最大列宽，若不指定则使用默认最大列宽
     */
    private final int maxColumnWidth;

    /**
     * 默认最大列宽
     */
    public static final int DEFAULT_MAX_COLUMN_WIDTH = 255;


    public CustomAutoColumnWidthWriteHandler() {
        this.maxColumnWidth = DEFAULT_MAX_COLUMN_WIDTH;
    }

    public CustomAutoColumnWidthWriteHandler(int maxColumnWidth) {
        if (maxColumnWidth <= 0) {
            this.maxColumnWidth = DEFAULT_MAX_COLUMN_WIDTH;
        } else {
            this.maxColumnWidth = Math.min(maxColumnWidth, DEFAULT_MAX_COLUMN_WIDTH);
        }
    }

    /**
     * 自动设置列宽
     */
    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        if (isHead || !CollectionUtils.isEmpty(cellDataList)) {
            HashMap<Integer, Double> maxColumnWidthMap = CACHE_COLUMN_WIDTH.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>());
            //获取列宽
            double columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                columnWidth = columnWidth > this.maxColumnWidth ? this.maxColumnWidth : columnWidth;
                Double maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), (int) (columnWidth * 256));
                }
            }
        }
    }

    /**
     * 计算列宽
     */
    private double dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return getContentWidth(cell.getStringCellValue());
        } else {
            CellData<?> cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1.0;
            }
            switch (type) {
                case STRING:
                    return getContentWidth(cellData.getStringValue());
                case BOOLEAN:
                    return getContentWidth(cellData.getBooleanValue().toString());
                case NUMBER:
                    return getContentWidth(cellData.getNumberValue().toString());
                case DATE:
                    return getContentWidth(cell.getLocalDateTimeCellValue().toString());
                default:
                    return -1.0;
            }
        }
    }

    /**
     * 字符串的宽度，按照unicode码点计算
     */
    private double getContentWidth(String str) {
        double length = 0.0;

        // 手动按照Unicode码点截取字符
        for (int i = 0; i < str.length(); ) {
            // 获取当前索引处的字符对应的Unicode码点
            int codePoint = str.codePointAt(i);

            // 计算该码点占用多少个char
            int charCount = Character.charCount(codePoint);
            // 将码点转为unicode字符
            String oneUnicodeChar = new String(Character.toChars(codePoint));

            // 该unicode字符占用多少个字节
            int charByteLength = oneUnicodeChar.getBytes(StandardCharsets.UTF_8).length;
            // 单字节字符
            if (charByteLength == 1) {
                // 单字节字符，，设置宽度为1
                length += 1;
            } else {
                // 多字节字符，设置宽度为2
                length += 2;
            }

            // 移动到下一个代码点
            i += charCount;
        }
        // 填充一个宽度
        return length + 1;
    }

}